namespace Microsoft.Sdc.Tasks.Configuration.InstalledProducts
{
	using System;
	using System.IO;
	using System.Xml;
	using Microsoft.Win32;
	using System.Reflection;
	using System.Xml.Serialization;

	/// <summary>
	/// Checks for installed Windows Components such as IIS or DNS
	/// </summary>
	internal class InstalledComponentsManager
	{
		/// <summary>
		/// Serialize the component list into an Xml string
		/// </summary>
		/// <param name="components"></param>
		/// <returns></returns>
		public string ConvertToXml(ComponentList components)
		{
			XmlSerializer serializer = new XmlSerializer(typeof(ComponentList));
			StringWriter sw = new StringWriter();
			serializer.Serialize(sw,components);
			return sw.ToString();
		}

		/// <summary>
		/// Deserialize from xml to ComponentList
		/// </summary>
		/// <param name="xml">Xml produced by the ConvertToXml method</param>
		/// <returns>List of Component objects</returns>
		public ComponentList ConvertFromXml(string xml)
		{
			XmlSerializer serializer = new XmlSerializer(typeof(ComponentList));			
			StringReader sr = new StringReader(xml);
			return (ComponentList)serializer.Deserialize(sr);
		}

		public bool IsInstalled(Criteria criteria, ComponentList components)
		{			
			return (GetMatchingInstalledComponent(criteria,components) != null);
		}
		
		public Component GetMatchingInstalledComponent(Criteria criteria, ComponentList components)
		{
			foreach (Component component in components)
			{
				try
				{
					if (criteria.IsMatch(component))
					{
						return component;
					}
				}
				catch (System.FormatException)
				{
					// Some products may have incompatible versions that cannot be compared
					// These will not match and will be ignored
				}
			}
			return null;
		}

		public ComponentList GetInstalledComponents()
		{
			Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Sdc.Tasks.Configuration.InstalledProducts.Components.xml");
			StreamReader sr =new StreamReader(s);					
			XmlDocument componentDetailsDom = new XmlDocument();
			componentDetailsDom.LoadXml(sr.ReadToEnd());
			sr.Close();

			ComponentList components = new ComponentList();

			string key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OC Manager\Subcomponents";
			RegistryKey subKey = Registry.LocalMachine.OpenSubKey(key);				
			string [] valueNames = subKey.GetValueNames();
			foreach (string valueName in valueNames)
			{				
				Object val = subKey.GetValue(valueName);
				if (val is int && Convert.ToBoolean(val))
				{					
					Component component = new Component();
					component.Id=valueName;

					// Look up a possible description in the components.xml file
					XmlElement componentDescription = (XmlElement)componentDetailsDom.SelectSingleNode("//component[@id='"+component.Id+"']/description");
					if (componentDescription != null)
					{
						component.Description = componentDescription.InnerText;
					}
					else
					{
						component.Description = valueName;
					}
					components.Add(component);
				}
			}
			subKey.Close();
			return components;
		}

		/// <summary>
		/// Convenience method to add a criterion for And-ing
		/// </summary>
        /// <param name="criteria"></param>
        /// <param name="criterion"></param>
		/// <returns></returns>
		public void AddForAnd(Criteria criteria, Criterion criterion)
		{	
			bool isFirst = (criteria.Count==0);
			criteria.Push(criterion.Value); // value to check against (RHS)
			criteria.Push(criterion.PropertyToExamine); // property info of product (LHS)
			criteria.Push(criterion.ComparisonChecker); // version comparison checks or string comparison
			criteria.Push(criterion.Operator); // not or none	

			if (! isFirst)
			{
				criteria.Push(BinaryLogicalOperator.And);
			}
		}		

		public InstalledComponentsManager()
		{
		}
	}
}
